package com.hm.ibsm.util;

import java.util.concurrent.TimeUnit;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;

import com.hm.common.exception.ErrorCode;
import com.hm.common.exception.ServiceException;
import com.hm.ibsm.auto.ManageProperties;

import lombok.extern.slf4j.Slf4j;

/**
 * @author shishun.wang
 * @date 2017年2月23日 上午10:14:26
 * @version 1.0
 * @param <T>
 * @describe
 */
@Slf4j
public final class RedisUtilLock {

	private RedisTemplate<String, Object> redisTemplate;

	private ManageProperties manageProperties;

	public RedisUtilLock(RedisTemplate<String, Object> redisTemplate, ManageProperties manageProperties) {
		this.redisTemplate = redisTemplate;
		this.manageProperties = manageProperties;
	}

	/**
	 * @param key
	 * @return true:当前key 锁定状态,false:当前key 解锁状态
	 */
	public boolean timeoutLock(String key) {
		return timeoutLock(key, manageProperties.getRedisConfig().getRequestLockTimeOut());
	}

	/**
	 * @param key
	 * @param clientRequstTimeOut
	 *            超时时间单位m
	 * @return true:当前key 锁定状态,false:当前key 解锁状态
	 */
	public boolean timeoutLock(String key, long clientRequstTimeOut) {
		Object lockTimeOut = redisTemplate.opsForValue().get(key);
		if (null == lockTimeOut) {
			JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
			Boolean execute = redisTemplate.execute(new RedisCallback<Boolean>() {
				public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
					return connection.setNX(key.getBytes(), serializer.serialize(System.currentTimeMillis() + clientRequstTimeOut));
				}
			});
			if (execute) {
				// 设置超时时间
				redisTemplate.expire(key, clientRequstTimeOut, TimeUnit.MILLISECONDS);
				log.info("{}锁定成功", key);
				return false;
			}
			return true;
		} else {
			long currentTime = System.currentTimeMillis();
			long currentKeyTimeOut = (long) lockTimeOut;
			if (currentTime < currentKeyTimeOut) {
				log.info("{}已经被锁定,解锁还需要:{}s", key, currentKeyTimeOut - currentTime);
				return true;
			}

			redisTemplate.delete(key);
			log.info("{}上次设定超时即将清除数据,并重新锁定");
			return timeoutLock(key, clientRequstTimeOut);
		}
	}

	/**
	 * @author shishun.wang
	 * @date 2017年2月23日 下午2:05:20
	 * @version 1.0
	 * @describe
	 */
	public class Client {

		public void checkTimeOutLock(String key) {
			if (timeoutLock(key)) {
				throw ServiceException.warning(ErrorCode.CLIENT_REQUEST_VISIT_FREQUENTLY);
			}
		}

	}
}
